------------------------------------------------------------------------------
--	GridStatusPriestAoe by Slaren
--    Thanks to Duugu for GridStatusCloseUp idea and original code
------------------------------------------------------------------------------

local QuickHealth = LibStub and LibStub("LibQuickHealth-2.0", true) -- don't error if not found
local MapFiles = LibStub("LibMapData-1.0") 
local HealComm = LibStub("LibHealComm-4.0") -- for healing modifying auras
local GridStatus = Grid:GetModule("GridStatus")

GridStatusPriestAoe = GridStatus:NewModule("GridStatusPriestAoe")
GridStatusPriestAoe.menuName = "Priest Aoe Healing"

-- Emphasize
local Emphasize = GridStatus:HasModule("GridStatusEmphasize") and GridStatus:GetModule("GridStatusEmphasize")
if Emphasize then
	local PriestAoe_Emphasize = Emphasize:NewModule("PriestAoe_Emphasize")
	local emphasize_options = {
		["coh_emphasize"] = {
			order = 1,
			type = "toggle",
			name = "Circle of Healing",
			desc = "Emphasize Circle of Healing",
			get = function () return GridStatusPriestAoe.db.profile.alert_priestaoe_coh.emphasize end,
			set = function (v) 	GridStatusPriestAoe.db.profile.alert_priestaoe_coh.emphasize = v end,
		},
		["poh_emphasize"] = {
			order = 2,
			type = "toggle",
			name = "Prayer of Healing",
			desc = "Emphasize Prayer of Healing",
			get = function () return GridStatusPriestAoe.db.profile.alert_priestaoe_poh.emphasize end,
			set = function (v) 	GridStatusPriestAoe.db.profile.alert_priestaoe_poh.emphasize = v end,
		},
		["hn_emphasize"] = {
			order = 3,
			type = "toggle",
			name = "Holy Nova",
			desc = "Emphasize Holy Nova",
			get = function () return GridStatusPriestAoe.db.profile.alert_priestaoe_hn.emphasize end,
			set = function (v) 	GridStatusPriestAoe.db.profile.alert_priestaoe_hn.emphasize = v end,
		},
	}
	function PriestAoe_Emphasize:OnInitialize()
		self.super.OnInitialize(self)
		
		self:RegisterOptions("Priest Aoe Healing", emphasize_options)
	end	
end
--

-- constants
local TALENT_TWIN_DISCIPLINES 	= 2
local TALENT_FOCUSED_POWER		= 16
local TALENT_HOLY_REACH			= 9
local TALENT_SPIRITUAL_HEALING 	= 16
local TALENT_BLESSED_RESILIENCE = 19
local TALENT_COH 				= 24
local TALENT_TEST_OF_FAITH 		= 25
local TALENT_DIVINE_PROVIDENCE 	= 26
local SPELLID_COH = 34861
local GLYPH_COH = 55675
local GLYPH_HN = 55683
local SET_PRIEST_HEALING_T10 = { 50766, 50765, 50769, 50768, 50767, 51260, 51179, 51261, 51178, 51262, 51177, 51263, 51176, 51264, 51175 }
local ICON_COH = "Interface\\Icons\\Spell_Holy_CircleOfRenewal"
local ICON_POH = "Interface\\Icons\\Spell_Holy_PrayerOfHealing02"
local ICON_HN = "Interface\\Icons\\Spell_Holy_HolyNova"
local PARTY_STATE_GROUP_COUNT = {
	["solo"] = 1,
	["party"] = 1,
	["heroic_raid"] = 5,
	["raid"] = 2,
	["bg"] = 8,
	["arena"] = 1
}

local SPELLMOD_COH = 1.5 / 3.5 * 0.5 * 1.88 
local SPELLMOD_POH = 3.0 / 3.5 * 0.5 * 0.66 * 1.88 -- 0.2798 * 1.88
local SPELLMOD_HN = 1.5 / 3.5 * 0.5 * 0.75 * 1.88  -- 0.1609 * 1.88

-- upvalues
local GridRoster = Grid:GetModule("GridRoster")
local GridFrame = Grid:GetModule("GridFrame")
local UnitGUID = UnitGUID
local GetPlayerMapPosition = GetPlayerMapPosition
local SetMapToCurrentZone = SetMapToCurrentZone
local GetCurrentMapDungeonLevel = GetCurrentMapDungeonLevel
local GetMapInfo = GetMapInfo
local UnitHealth = QuickHealth and QuickHealth.UnitHealth or UnitHealth
local UnitHealthMax = UnitHealthMax
local UnitIsVisible = UnitIsVisible
local UnitIsDeadOrGhost = UnitIsDeadOrGhost
local UnitIsConnected = UnitIsConnected 
local UnitIsEnemy = UnitIsEnemy
local UnitIsCharmed = UnitIsCharmed
local math_min = math.min
local math_max = math.max
local math_floor = math.floor
local tinsert = table.insert
local tsort = table.sort
local GetPlayerHealingMod = HealComm.GetPlayerHealingMod and HealComm.GetPlayerHealingMod or function() return 1 end


-- local data
local settings
local settings_coh
local settings_poh
local settings_hn
local num_enabled = 0
local update_frame = CreateFrame("Frame")
local coh_prev_best = {
	uid = nil,
	guid = nil,
	amount = -1
}
local coh_targets
local coh_base
local coh_coef_madd
local coh_coef_m
local poh_base
local poh_coef_madd
local poh_coef_m
local hn_base
local hn_coef_madd
local hn_coef_m
local update_timer = 0
local coh_testrange_sq
local poh_testrange_sq
local hn_testrange_sq
local num_groups
local test_of_faith_mul
local roster
local player_group


GridStatusPriestAoe.defaultDB = {
	debug = false,
	cycle_time = 0.2,
	first_groups_only = true,

	alert_priestaoe_coh = {
		-- coh
		enable = true,
		color = { r = 1, g = 1, b = 0, a = 1 },
		priority = 99,
		range = true,
		
		enable_if_have = true,
		threshold = 10000,
		hidecd = true,
		emphasize = true,
	},
	alert_priestaoe_poh = {
		-- poh
		enable = true,
		color = { r = 1, g = 1, b = 1, a = 1 },
		priority = 97,
		range = true,
		
		always_best = false,
		threshold = 15000,
		emphasize = false,
	},
	alert_priestaoe_hn = {
		-- holy nova
		enable = true,
		color = { r = 0, g = 1, b = 0, a = 1 },
		priority = 98,

		threshold = 7500,
		emphasize = false,
	}
}

local coh_options = {
	["coh_threshold"] = {
		order = 100,
		type = "range",
		name = "Heal threshold",
		desc = "Threshold for heal amount",
		max = 20000,
		min = 1,
		step = 1,
		get = function () return GridStatusPriestAoe.db.profile.alert_priestaoe_coh.threshold end,
		set = function (v) GridStatusPriestAoe.db.profile.alert_priestaoe_coh.threshold = v end,
	},
	["coh_enable_if_have"] = {
		order = 101,
		type = "toggle",
		name = "Show only while having the talent",
		desc = "Enable this if you want to hide this status while using a different talent spec",
		get = function () return GridStatusPriestAoe.db.profile.alert_priestaoe_coh.enable_if_have end,
		set = function (v) GridStatusPriestAoe.db.profile.alert_priestaoe_coh.enable_if_have = v end,
	},
	["coh_hidecd"] = {
		order = 102,
		type = "toggle",
		name = "Hide while on cooldown",
		desc = "Hide while Circle of Healing is on cooldown",
		get = function () return GridStatusPriestAoe.db.profile.alert_priestaoe_coh.hidecd end,
		set = function (arg) GridStatusPriestAoe.db.profile.alert_priestaoe_coh.hidecd = arg end,
	}
}

local poh_options = {
	["poh_threshold"] = {
		order = 100,
		type = "range",
		name = "Heal threshold",
		desc = "Threshold for heal amount",
		max = 20000,
		min = 1,
		step = 1,
		get = function () return GridStatusPriestAoe.db.profile.alert_priestaoe_poh.threshold end,
		set = function (v) GridStatusPriestAoe.db.profile.alert_priestaoe_poh.threshold = v end,
	},
	["poh_enablebest"] = {
		order = 101,
		type = "toggle",
		name = "Always show best target in each group",
		desc = "Show best target in each group, even if the heal amount is below the threshold (minimum 3 targets)",
		get = function () return GridStatusPriestAoe.db.profile.alert_priestaoe_poh.always_best end,
		set = function (v) GridStatusPriestAoe.db.profile.alert_priestaoe_poh.always_best = v end,
	},
}

local hn_options = {
	["range"] = false,
	["hn_threshold"] = {
		order = 100,
		type = "range",
		name = "Heal threshold",
		desc = "Threshold for heal amount",
		max = 20000,
		min = 1,
		step = 1,
		get = function () return GridStatusPriestAoe.db.profile.alert_priestaoe_hn.threshold end,
		set = function (v) GridStatusPriestAoe.db.profile.alert_priestaoe_hn.threshold = v end,
	}
}

function GridStatusPriestAoe:OnInitialize()
	self.super.OnInitialize(self)
	
	-- options
	GridStatusPriestAoe.extraOptions = {
		["frequency"] = {
			order = 100,
			type = "range",
			name = "Refresh time",
			desc = "Seconds between status refreshes",
			get = function () return GridStatusPriestAoe.db.profile.cycle_time end,
			set = function (v) GridStatusPriestAoe.db.profile.cycle_time = v end,
			min = 0.01,
			max = 1,
			step = 0.01,
		},
		["first_groups_only"] = {
			order = 101,
			type = "toggle",
			name = "Check only first groups",
			desc = "Depending on the raid size, checks only the first groups to save time",
			get = function () return GridStatusPriestAoe.db.profile.first_groups_only end,
			set = function (v) GridStatusPriestAoe.db.profile.first_groups_only = v; GridStatusPriestAoe:UpdateNumGroups() end,
		},
	}	
	
	self:RegisterStatus("alert_priestaoe_coh", "Priest Aoe - Circle of Healing", coh_options)
	self:RegisterStatus("alert_priestaoe_poh", "Priest Aoe - Prayer of Healing", poh_options)
	self:RegisterStatus("alert_priestaoe_hn", "Priest Aoe - Holy Nova", hn_options)
	
	--
	WorldMapFrame:Show()
	WorldMapFrame:Hide()
	
	settings = self.db.profile
	settings_coh = self.db.profile.alert_priestaoe_coh
	settings_poh = self.db.profile.alert_priestaoe_poh
	settings_hn = self.db.profile.alert_priestaoe_hn
end

function GridStatusPriestAoe:OnStatusEnable(status)
	if status == "alert_priestaoe_coh" or status == "alert_priestaoe_poh" or status == "alert_priestaoe_hn" then
		num_enabled = num_enabled + 1
		if num_enabled == 1 then
			self:UpdateRoster()
			self:UpdateNumGroups()

			self:RegisterEvent("PLAYER_TALENT_UPDATE", "UpdateTalents")
			self:RegisterEvent("PLAYER_EQUIPMENT_CHANGED", "UpdateCoefs")
			self:RegisterEvent("Grid_PartyTransition", "UpdateNumGroups")
			self:RegisterEvent("PARTY_MEMBERS_CHANGED", "UpdateRoster")
			self:RegisterEvent("RAID_ROSTER_UPDATE", "UpdateRoster")
			-- check talents the first frame, then change OnUpdate script to the real function
			update_frame:SetScript("OnUpdate",
				function()
					self:UpdateTalents()
					update_frame:SetScript("OnUpdate", function(_, elapsed) return self:OnUpdate(elapsed) end)
				end)
		end
	end
end

function GridStatusPriestAoe:OnStatusDisable(status)
	if num_enabled > 0 then
		if status == "alert_priestaoe_coh" then
			self.core:SendStatusLostAllUnits("alert_priestaoe_coh")
			if Emphasize then Emphasize:TempDeemphasizeAllUnits("priestaoe_coh") end
			num_enabled = num_enabled - 1
		elseif status == "alert_priestaoe_poh" then
			self.core:SendStatusLostAllUnits("alert_priestaoe_poh")
			if Emphasize then Emphasize:TempDeemphasizeAllUnits("priestaoe_poh") end
			num_enabled = num_enabled - 1
		elseif status == "alert_priestaoe_hn" then
			self.core:SendStatusLostAllUnits("alert_priestaoe_hn")
			if Emphasize then Emphasize:TempDeemphasizeAllUnits("priestaoe_hn") end
			num_enabled = num_enabled - 1
		end
		
		if num_enabled == 0 then
			update_frame:SetScript("OnUpdate", nil)
			self:UnregisterEvent("PLAYER_TALENT_UPDATE")
			self:UnregisterEvent("PLAYER_EQUIPMENT_CHANGED")
			self:UnregisterEvent("Grid_PartyTransition")
			self:UnregisterEvent("PARTY_MEMBERS_CHANGED")
			self:UnregisterEvent("RAID_ROSTER_UPDATE")
		end
	end
end

function GridStatusPriestAoe:UpdateRoster()
	roster = { [1] = {}, [2] = {}, [3] = {}, [4] = {}, [5] = {}, [6] = {}, [7] = {}, [8] = {} }
	
	if GetNumRaidMembers() == 0 then
		tinsert(roster[1], "player")
		player_group = 1
		for i = 1, 4 do
			local unitid = "party" .. i
			if UnitExists(unitid) then
				tinsert(roster[1], unitid)
			end
		end
	else
		for i = 1, GetNumRaidMembers() do
			local name, _, group = GetRaidRosterInfo(i)
			local unitid = "raid" .. i
			if name and UnitExists(unitid) then
				tinsert(roster[group], unitid)
				if UnitIsUnit("player", unitid) then
					player_group = group
				end
			end
		end
	end
end

function GridStatusPriestAoe:UpdateTalents()
	-- check coh
	local _, _, _, _, coh = GetTalentInfo(2, TALENT_COH)
	
	if coh > 0 then
		-- check if player has coh glyph
		if self:HasGlyph(GLYPH_COH) then
			coh_targets = 6
		else
			coh_targets = 5
		end	
	else
		coh_targets = nil
	end
	-- lost coh? clear status
	if settings_coh.enable and settings_coh.enable_if_have and not coh_targets then
		self:ClearCohStatus()
	end
	
	-- update ranges
	local _, _, _, _, holy_reach = GetTalentInfo(2, TALENT_HOLY_REACH)
	holy_reach = 1 + holy_reach / 10
	coh_testrange_sq = (15 * holy_reach) ^ 2
	poh_testrange_sq = (30 * holy_reach) ^ 2
	hn_testrange_sq = (10 * holy_reach) ^ 2

	-- update spell coefs
	self:UpdateCoefs()
end

function GridStatusPriestAoe:UpdateNumGroups()
	num_groups = (settings.first_groups_only and PARTY_STATE_GROUP_COUNT[GridRoster:GetPartyState()]) or 8
end

function GridStatusPriestAoe:ClearCohStatus()
	if coh_prev_best.guid then
		self.core:SendStatusLost(coh_prev_best.guid, "alert_priestaoe_coh")
		coh_prev_best.guid = nil
		coh_prev_best.uid = nil
		coh_prev_best.amount = -1
		
		if Emphasize then Emphasize:TempDeemphasizeAllUnits("priestaoe_coh") end
	end
end

function GridStatusPriestAoe:ClearPohStatus()
	self.core:SendStatusLostAllUnits("alert_priestaoe_poh")
	if Emphasize then Emphasize:TempDeemphasizeAllUnits("priestaoe_poh") end
end

function GridStatusPriestAoe:ClearHnStatus()
	self.core:SendStatusLost(UnitGUID("player"), "alert_priestaoe_hn")
	if Emphasize then Emphasize:TempDeemphasizeUnit(UnitGUID("player"), "priestaoe_hn") end
end

-- debug
local update_init_time = nil
local update_time = nil

local update_this_start_time = 0
local debug_frame = nil
local debug_fs = nil

local function start_timing()
	if not debug_frame then
		debug_frame = CreateFrame("Frame", "gridstatuspriestaoe_performance_monitor", UIParent)	
		debug_fs = debug_frame:CreateFontString(nil, "OVERLAY")
		debug_fs:SetFont(STANDARD_TEXT_FONT, 18,"THICKOUTLINE")
		debug_fs:SetPoint("LEFT", UIParent, "LEFT")
		debug_fs:SetText("GridStatusPriestAoe performance monitor")
	end
	local ctime = GetTime()
	update_this_start_time = ctime
	if update_init_time == nil then
		update_init_time = ctime
		update_time = 0
	end
end

local function end_timing()
	local ctime = GetTime()
	update_time = update_time + (ctime - update_this_start_time)
	
	local elapsed = ctime - update_init_time
	if elapsed >= 5 then
		debug_fs:SetText(string.format("%0.2f%%", update_time/elapsed*100))
		-- debug_fs:SetText(tostring(GetPlayerHealingMod()))
		
		update_time = 0
		update_init_time = nil
	end
end
-- debug end

function GridStatusPriestAoe:OnUpdate(elapsed)
	update_timer = update_timer + elapsed

	-- check for coh cooldown here to update faster
	if coh_prev_best.guid and settings_coh.enable and settings_coh.hidecd and not self:IsCohReady() then
		self:ClearCohStatus()
	end	

	if update_timer >= settings.cycle_time then
		update_timer = 0
		if settings.debug then start_timing() end
		GridStatusPriestAoe:RefreshAll()
		if settings.debug then end_timing() end
	end
end

local refresh_state = {
	player_x = 0,
	player_y = 0,
	player_data = nil	
}

local function DistanceSq(x1, y1, x2, y2)
	local xx = x2 - x1
	local yy = y2 - y1
	local dist_sq = xx*xx + yy*yy
	
	return dist_sq
end

local function IsValidTarget(unitid)
	return not UnitIsDeadOrGhost(unitid) and
				UnitIsConnected(unitid) and 
				UnitIsVisible(unitid) and
				not (UnitIsCharmed(unitid) and UnitIsEnemy("player", unitid))
end

function GridStatusPriestAoe:RefreshAll()
	local refresh_state = refresh_state

	-- check player position
	refresh_state.player_x, refresh_state.player_y = GetPlayerMapPosition("player")
	if refresh_state.player_x <= 0 and refresh_state.player_y <= 0 then
		if WorldMapFrame:IsVisible() then
			return
		end
		-- carbonite blinking map workaround
		local f = GetMouseFocus()
		while f do
			if f:GetName() == "NxMap1" then
				return
			end
			f = f:GetParent()
		end

		SetMapToCurrentZone()
		refresh_state.player_x, refresh_state.player_y = GetPlayerMapPosition("player") 
	end
 	
	-- continue only if map supported
	if (refresh_state.player_x > 0 or refresh_state.player_y > 0) then
		local fileName = GetMapInfo()
		local currentLevel = GetCurrentMapDungeonLevel()
		local map_width, map_height = MapFiles:MapArea(fileName, currentLevel) 
		if map_width ~= 0 and map_height ~= 0 then
			-- cache player data
			refresh_state.player_x = refresh_state.player_x * map_width
			refresh_state.player_y = refresh_state.player_y * map_height
			refresh_state.player_data = {}
			for group = 1, num_groups do
				for _, unitid in pairs(roster[group]) do
					local cx, cy = GetPlayerMapPosition(unitid)
					if (cx ~= 0 or cy ~= 0) and IsValidTarget(unitid) then
						local health = UnitHealth(unitid)
						local health_max = UnitHealthMax(unitid)
						local cguid = UnitGUID(unitid)
						local percent = health / health_max

						refresh_state.player_data[unitid] = {
							guid = cguid,
							x = cx * map_width,
							y = cy * map_height,
							deficit = health_max - health,
							percent = percent,
							healmod = HealComm:GetHealModifier(cguid) * (percent <= 0.5 and test_of_faith_mul or 1)
						}
					end
				end
			end

			if settings_coh.enable and 
				(not settings_coh.hidecd or self:IsCohReady()) and
				(not settings_coh.enable_if_have or coh_targets) then
				self:RefreshCoH()
			end
			
			if settings_poh.enable then
				self:RefreshPoH()
			end
			
			if settings_hn.enable then
				self:RefreshHN()
			end
		end
	end
end
					
local function SortTargets(x, y)
	return x.percent < y.percent
end

function GridStatusPriestAoe:RefreshCoH()
	local refresh_state = refresh_state

	local coh_avg = self:GetCohAvg() * GetPlayerHealingMod()
	
	local coh_best_uid = nil
	local coh_best_amount = -1
	local coh_best_pdata = nil
	local coh_best_dists = nil
	
	-- check all	
	for group = 1, num_groups do
		for _, unitid in pairs(roster[group]) do
			if not settings_coh.range or GridFrame:UnitInRange(unitid, 40) then
				local p1 = refresh_state.player_data[unitid]
				if p1 then
					local targets = {}
					for tgroup = 1, num_groups do
						for _, tunitid in pairs(roster[tgroup]) do
							local p2 = refresh_state.player_data[tunitid]
							if p2 then
								local dist = DistanceSq(p1.x, p1.y, p2.x, p2.y)
								if dist <= coh_testrange_sq then
									local amount = math_min(p2.deficit, coh_avg * p2.healmod)
									if amount > 0 then
										tinsert(targets, { amount = amount, percent = p2.percent, dist = dist })
									end
								end
							end
						end
					end

					-- order targets, get best
					tsort(targets, SortTargets)
					local dists = 0
					local hsum = 0
					for ti = 1, math_min(coh_targets or 5, #targets) do
						hsum = hsum + targets[ti].amount
						dists = math_max(dists, targets[ti].dist)
					end
					-- dists = dists / math_min(coh_targets or 5, #targets)

					-- select best, buy try to keep same target
					if hsum > coh_best_amount or 
							(hsum == coh_best_amount and (
								unitid == coh_prev_best.uid or 
								dists < coh_best_dists or
								(coh_best_uid ~= coh_prev_best.uid and p1.deficit > coh_best_pdata.deficit)
								)) then
						coh_best_uid = unitid
						coh_best_amount = hsum
						coh_best_pdata = p1
						coh_best_dists = dists
					end
				end
			end
		end
	end
		
	-- send status to best
	if coh_best_uid ~= coh_prev_best.uid or coh_best_amount < settings_coh.threshold then
		self:ClearCohStatus()
	end

	if coh_best_uid and coh_best_amount >= settings_coh.threshold then
		coh_prev_best.uid = coh_best_uid
		coh_prev_best.guid = coh_best_pdata.guid
		coh_prev_best.amount = coh_best_amount
		self.core:SendStatusGained(	coh_prev_best.guid, 
									"alert_priestaoe_coh",
									settings_coh.priority,
									nil,
									settings_coh.color,
									tostring(math_floor(coh_best_amount + 0.5)),
									1, 
									nil,
									ICON_COH)
									
		if settings_coh.emphasize and Emphasize then
			Emphasize:TempEmphasizeUnit(coh_prev_best.guid, "priestaoe_coh")
		end
	end
end

function GridStatusPriestAoe:RefreshPoH()
	local refresh_state = refresh_state

	self:ClearPohStatus()
	
	local poh_avg = self:GetPohAvg() * GetPlayerHealingMod()

	-- check all
	for group = 1, num_groups do
		local group_best = { uid = nil, amount = -1, targets = -1, pdata = nil }
		
		for _, unitid in pairs(roster[group]) do
			if not settings_poh.range or GridFrame:UnitInRange(unitid, 40) then
				local amount = 0
				local targets = 0
				local p1 = refresh_state.player_data[unitid]
				if p1 then
					for _, tunitid in pairs(roster[group]) do
						local p2 = refresh_state.player_data[tunitid]
						if p2 then
							local dist = DistanceSq(p1.x, p1.y, p2.x, p2.y)
							if dist <= poh_testrange_sq then
								amount = amount + math_min(p2.deficit, poh_avg * p2.healmod)
								targets = targets + 1
							end
						end
					end
					
					if amount > group_best.amount or (amount == group_best.amount and targets > group_best.targets) then
						group_best.amount = amount
						group_best.uid = unitid
						group_best.targets = targets
						group_best.pdata = p1
					end
				end
			end
		end
		
		if group_best.uid and ((settings_poh.always_best and group_best.targets >= 3) or group_best.amount >= settings_poh.threshold) then
			local text = (group_best.amount >= settings_poh.threshold) and math_floor(group_best.amount + 0.5) or group_best.targets
			self.core:SendStatusGained(	group_best.pdata.guid,
										"alert_priestaoe_poh",
										settings_poh.priority,
										nil,
										settings_poh.color,
										tostring(text),
										1, 
										nil,
										ICON_POH)
			if settings_poh.emphasize and Emphasize then
				Emphasize:TempEmphasizeUnit(group_best.pdata.guid, "priestaoe_poh")
			end
		end
	end
end

-- Holy Nova
function GridStatusPriestAoe:RefreshHN()
	local refresh_state = refresh_state

	self:ClearHnStatus()
	
	if settings.first_groups_only and player_group > num_groups then
		return
	end

	local hn_avg = self:GetHnAvg() * GetPlayerHealingMod()

	-- check all
	local amount = 0
	for _, tunitid in pairs(roster[player_group]) do
		local p2 = refresh_state.player_data[tunitid]
		if p2 then
			local dist = DistanceSq(refresh_state.player_x, refresh_state.player_y, p2.x, p2.y)
			if dist <= hn_testrange_sq then
				amount = amount + math_min(p2.deficit, hn_avg * p2.healmod)
			end
		end
	end

	if amount >= settings_hn.threshold then
		self.core:SendStatusGained(	UnitGUID("player"),
									"alert_priestaoe_hn",
									settings_hn.priority,
									nil,
									settings_hn.color,
									tostring(math_floor(amount + 0.5)),
									1, 
									nil,
									ICON_HN)
		if settings_hn.emphasize and Emphasize then
			Emphasize:TempEmphasizeUnit(UnitGUID("player"), "priestaoe_hn")
		end
	end
end

function GridStatusPriestAoe:HasGlyph(gid)
	for i = 1, 6 do
		local _, _, id = GetGlyphSocketInfo(i)
		if id and gid == id then 
			return true
		end
	end
	return false
end

function GridStatusPriestAoe:GetSetAmount(set)
	local amount = 0
	for _, itemID in ipairs(set) do
		if IsEquippedItem(itemID) then
			amount = amount + 1
		end
	end
	return amount
end		

function GridStatusPriestAoe:IsCohReady()
	local start, duration = GetSpellCooldown(SPELLID_COH)

	-- ignore global cooldown, show if ready or less than 0.5 secs of cooldown remaining
	if start == 0 or duration <= 1.5 or ((duration - (GetTime() - start)) <= 0.5) then
		return true
	end
	
	return false
end

function GridStatusPriestAoe:UpdateCoefs()
	-- get talents
	local _, _, _, _, twin_disciplines = GetTalentInfo(1, TALENT_TWIN_DISCIPLINES)
	local _, _, _, _, focused_power = GetTalentInfo(1, TALENT_FOCUSED_POWER)
	local _, _, _, _, spiritual_healing = GetTalentInfo(2, TALENT_SPIRITUAL_HEALING)
	local _, _, _, _, blessed_resilience = GetTalentInfo(2, TALENT_BLESSED_RESILIENCE)
	local _, _, _, _, test_of_faith = GetTalentInfo(2, TALENT_TEST_OF_FAITH) -- multiplicative
	local _, _, _, _, divine_providence = GetTalentInfo(2, TALENT_DIVINE_PROVIDENCE)
	
	-- Test of Faith
	test_of_faith_mul = 1 + test_of_faith * 0.04
	
	-- Circle of Healing
	-- 958 to 1058
	coh_base = 1008

	coh_coef_madd =
			spiritual_healing * 0.02 +
			twin_disciplines * 0.01 +
			divine_providence * 0.02

	coh_coef_m = 
		(1 + blessed_resilience * 0.01)

	if self:GetSetAmount(SET_PRIEST_HEALING_T10) >= 4 then
		coh_coef_madd = coh_coef_madd + 0.1
	end
	

	-- Prayer of Healing
	-- 2091+18 to 2209+19
	poh_base = 2169

	poh_coef_madd = 
			spiritual_healing * 0.02 +
			divine_providence * 0.02

	poh_coef_m = 
			(1 + blessed_resilience * 0.01) *
			(1 + focused_power * 0.02)
	

	-- Holy Nova
	-- 713 to 827
	hn_base = 770

	hn_coef_madd = 
			spiritual_healing * 0.02 +
			twin_disciplines * 0.01 +
			divine_providence * 0.02

	hn_coef_m = 
			(1 + blessed_resilience * 0.01) *
			(1 + focused_power * 0.02)

	if self:HasGlyph(GLYPH_HN) then
		hn_coef_madd = hn_coef_madd + 0.2
	end
end

function GridStatusPriestAoe:GetCohAvg()
	local coef = (1 + coh_coef_madd) * coh_coef_m
	local spellcrit = GetSpellCritChance(2)
	local spellbonus = GetSpellBonusHealing()
	
	local total = (coh_base + spellbonus * SPELLMOD_COH) * coef
	total = total * (1 + spellcrit / 200.0) -- apply crit

	return math_floor(total + 0.5)
end

function GridStatusPriestAoe:GetPohAvg()
	local coef = (1 + poh_coef_madd) * poh_coef_m
	local spellcrit = GetSpellCritChance(2)
	local spellbonus = GetSpellBonusHealing()
	
	local total = (poh_base + (spellbonus * SPELLMOD_POH)) * coef
	total = total * (1 + spellcrit / 200.0) -- apply crit
	
	return math_floor(total + 0.5)
end

function GridStatusPriestAoe:GetHnAvg()
	local coef = (1 + hn_coef_madd) * hn_coef_m
	local spellcrit = GetSpellCritChance(2)
	local spellbonus = GetSpellBonusHealing()
	
	local total = (hn_base + (spellbonus * SPELLMOD_HN)) * coef
	total = total * (1 + spellcrit / 200.0) -- apply crit

	return math_floor(total + 0.5)
end
